package gol;

import gol.GameOfLife.Cell;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;

import javax.swing.*;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTextArea;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;

/**
 * Controller of the GameOfLife class.
 * 
 * @author Jonathan Amery-Behr, Modified from GameOfLifeController.java by Kevin
 *         Workman
 */
public class GOLController {

	private GameOfLife gol;
	private JPanel panel = new JPanel();
	private JButton timerButton;
	private JSlider speedSlider, zoomSlider;
	private JComboBox patternComboBox;
	private JComboBox sizeComboBox;
	private JComboBox ruleComboBox;
	private Boolean periodPrompt = true;

	public JComboBox nBorder, eBorder, sBorder, wBorder;
	public JLabel speedLabel, zoomLabel;
	public JCheckBox limiterCheckbox, scrollableCheckbox, gridCheckbox;

	private Timer nextTimer = new Timer(0, new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			iterate();
		}
	});

	public GOLController(GameOfLife gol) {

		this.gol = gol;

		Dimension controlDimension = new Dimension(100, 25);

		timerButton = new JButton("\u25BA");
		timerButton
				.setToolTipText("Query OEIS.org with a number of derived sequences");
		timerButton.setMinimumSize(controlDimension);
		timerButton.setMaximumSize(controlDimension);
		timerButton.setPreferredSize(controlDimension);
		timerButton.setFocusable(false);
		timerButton.setAlignmentX(JComponent.CENTER_ALIGNMENT);
		timerButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if (nextTimer.isRunning()) {
					pause();
				} else {
					start();
				}
			}
		});

		final JButton searchButton = new JButton();
		searchButton.setText("Find");
		searchButton
				.setToolTipText("Incrementally increase grid size and eval until death or periodicity");
		searchButton.setMargin(new Insets(1, 1, 1, 1));

		searchButton.setMinimumSize(new Dimension(20, controlDimension.height));

		searchButton.setFocusable(false);
		searchButton.setAlignmentX(JComponent.CENTER_ALIGNMENT);
		searchButton.addActionListener(new ActionListener() {

			Boolean loop = false;
			hunt Hunter;
			int initialColumns = GOLController.this.gol.columns;

			@Override
			public void actionPerformed(ActionEvent arg0) {
				if (loop == false) {
					searchButton.setText("Stop");
					initialColumns = GOLController.this.gol.columns;
					Hunter = new hunt();

					loop = true;
					periodPrompt = false;
					GOLController.this.gol.drawGrid = false;
					GOLController.this.gol.performUndo = false;
					
					Hunter.start();
				} else if (loop == true) {
					GOLController.this.gol.clearHistories();
					searchButton.setText("Find");

					loop = false;
					periodPrompt = true;
					GOLController.this.gol.drawGrid = true;
					GOLController.this.gol.performUndo = true;

					ArrayList<Integer> periods = Hunter.getPeriods();
					try {
						Hunter.end();
						Hunter.join();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

					int i = initialColumns;

					JTextArea huntTextArea = new JTextArea(
							"Life/Death search results::\n");

					JScrollPane sp = new JScrollPane(huntTextArea);
					sp.setPreferredSize(new Dimension(800, 500));

					for (Integer period : periods) {

						if (period > 0)
							huntTextArea.setText(huntTextArea.getText() + i
									+ ": Infinite Life, Period: " + period
									+ "\n");
						else
							huntTextArea.setText(huntTextArea.getText() + i
									+ ": Death\n");
						i++;
						i++;
					}

					JOptionPane.showMessageDialog(panel, sp, "Hunt Response",
							JOptionPane.PLAIN_MESSAGE);

				}
			}

			class hunt extends Thread {

				Cell[][] initialState;
				ArrayList<Integer> periods;
				volatile Boolean loop;

				public hunt() {
					periods = new ArrayList<Integer>();
					loop = true;
				}

				public void end() {
					loop = false;

				}

				public ArrayList<Integer> getPeriods() {
					return periods;
				}

				public void run() {
					initialState = GOLController.this.gol.getCellArray();
					while (loop) {
						
						int columns = GOLController.this.gol.columns;
						int rows = GOLController.this.gol.rows;
						
//						if (i++ >= 4999) {
//								System.out.println("life for at least " + i + " Generations, C:" + columns + " G:"+ GOLController.this.gol.generations);
//								periods.add(i);
//								GOLController.this.gol.setCellArray(initialState);
//								GOLController.this.gol.setGridSize(columns + 2,
//										rows + 2);
//								i=0;
//							}


						int period = iterate();

						if ((period > 0)) {
							System.out.println("life, P:" + period+ " C:" + columns + " G:"+ GOLController.this.gol.generations);
							periods.add(period);
							GOLController.this.gol.setCellArray(initialState);
							GOLController.this.gol.setGridSize(columns + 2,
									rows + 2);
						} else if ((GOLController.this.gol.isEmpty())) {
							System.out.println("death after " + GOLController.this.gol.generations + " generations");
							periods.add(period);
							GOLController.this.gol.setCellArray(initialState);
							GOLController.this.gol.setGridSize(columns + 2,
									rows + 2);
						}

					}
				}

			};
		});

		final JButton stepButton = new JButton("Step");
		stepButton.setMinimumSize(controlDimension);
		stepButton.setMaximumSize(controlDimension);
		stepButton.setPreferredSize(controlDimension);
		stepButton.setFocusable(false);
		stepButton.setAlignmentX(JComponent.CENTER_ALIGNMENT);
		stepButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				pause();
				iterate();
			}
		});

		final JButton undoButton = new JButton("Undo");
		undoButton.setMinimumSize(controlDimension);
		undoButton.setMaximumSize(controlDimension);
		undoButton.setPreferredSize(controlDimension);
		undoButton.setFocusable(false);
		undoButton.setAlignmentX(JComponent.CENTER_ALIGNMENT);
		undoButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				pause();
				GOLController.this.gol.undoGeneration();
			}
		});
		if (!GameOfLife.ENABLEUNDO)
			undoButton.setVisible(false);

		final JButton saveButton = new JButton("Save");
		saveButton.setMinimumSize(controlDimension);
		saveButton.setMaximumSize(controlDimension);
		saveButton.setPreferredSize(controlDimension);
		saveButton.setFocusable(false);
		saveButton.setAlignmentX(JComponent.CENTER_ALIGNMENT);
		saveButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				String filename = JOptionPane.showInputDialog(
						"Enter a filename", "") + ".lif";
				System.out.println("'" + filename + "'");
				if (!filename.equals("null.lif")) {
					saveFile("#" + filename);
					patternComboBox.insertItemAt("#" + filename, 1);

				}
			}
		});

		final JButton seqButton = new JButton();
		seqButton.setText("?");
		seqButton.setMargin(new Insets(1, 1, 1, 1));

		seqButton.setMinimumSize(new Dimension(20, controlDimension.height));

		seqButton.setFocusable(false);
		seqButton.setAlignmentX(JComponent.CENTER_ALIGNMENT);
		seqButton.addActionListener(new ActionListener() {

			class seqRequest extends Thread {
				JTextArea oeisTextArea;
				String seq;
				String title;

				public seqRequest(JTextArea oeisTextArea, String seq,
						String title) {
					this.seq = seq;
					this.title = title;
					this.oeisTextArea = oeisTextArea;
				}

				public void run() {
					try {

						URLConnection con = new URL(
								"http://oeis.org/search?fmt=text&q=" + seq)
								.openConnection();
						System.out.println(title + " " + seq);

						Reader r = new InputStreamReader(con.getInputStream());
						String str = "Sequence query: " + seq;
						String line = "";
						BufferedReader bw = new BufferedReader(r);
						while ((line = bw.readLine()) != null) {
							if (line.startsWith("%S"))
								str = str + "\n\t" + line.substring(3);
							else if (line.startsWith("%T"))
								str = str + "\n\t" + line.substring(3);
							else if (line.startsWith("%N"))
								str = str + "\n\t" + "Name: "
										+ line.substring(3) + "\n";
						}
						str = str + "\nEnd of " + title;
						str = oeisTextArea.getText() + "\n\n" + title + "\n"
								+ str;

						oeisTextArea.setText(str);
					} catch (Exception e) {
					}
					highlightSequence(oeisTextArea, seq);

				}
			}

			private void highlightSequence(JTextArea TextArea, String seq) {

				int index = TextArea.getText().indexOf(seq);
				while (index >= 0) {
					int len = seq.length();
					try {
						TextArea.getHighlighter().addHighlight(index,
								index + len, DefaultHighlighter.DefaultPainter);
					} catch (Exception e) {
						e.printStackTrace();
					}
					index = TextArea.getText().indexOf(seq, index + len);
				}
			}

			@Override
			public void actionPerformed(ActionEvent e) {
				pause();

				JTextArea oeisTextArea = new JTextArea(
						"Waiting for oeis.org responses::");

				String totalSeq = GOLController.this.gol.getCellCountHistory();
				String growthSeq = GOLController.this.gol.getNetGrowthHistory();
				String birthSeq = GOLController.this.gol.getBirthHistory();
				String deathSeq = GOLController.this.gol.getDeathHistory();
				String widthSeq = GOLController.this.gol.getWidthHistory();
				String heightSeq = GOLController.this.gol.getHeightHistory();
				String areaSeq = GOLController.this.gol.getAreaHistory();
				String symmetrySeq = GOLController.this.gol
						.getSymmetryHistory();
				String xsymmetrySeq = GOLController.this.gol
						.getXSymmetryHistory();
				String ysymmetrySeq = GOLController.this.gol
						.getYSymmetryHistory();

				// seqs = new String[]{totalSeq, growthSeq, birthSeq, deathSeq,
				// widthSeq, heightSeq, areaSeq, symmetrySeq, xsymmetrySeq,
				// ysymmetrySeq};

				if (!growthSeq.isEmpty())
					new seqRequest(oeisTextArea, growthSeq,
							"Net Growth History").start();
				else
					oeisTextArea.setText(oeisTextArea.getText()
							+ "\nNet Growth History is trival");
				if (!birthSeq.isEmpty())
					new seqRequest(oeisTextArea, birthSeq, "Birth History")
							.start();
				else
					oeisTextArea.setText(oeisTextArea.getText()
							+ "\nBirth History is trival");
				if (!deathSeq.isEmpty())
					new seqRequest(oeisTextArea, deathSeq, "Death History")
							.start();
				else
					oeisTextArea.setText(oeisTextArea.getText()
							+ "\nDeath History is trival");
				if (!totalSeq.isEmpty())
					new seqRequest(oeisTextArea, totalSeq,
							"Total Cell count History").start();
				else
					oeisTextArea.setText(oeisTextArea.getText()
							+ "\nTotal Cell count is trival");
				if ((!widthSeq.isEmpty()))
					new seqRequest(oeisTextArea, widthSeq, "Width History")
							.start();
				else
					oeisTextArea.setText(oeisTextArea.getText()
							+ "\nWidth History is trival");
				if ((!heightSeq.isEmpty()) && !widthSeq.equals(heightSeq))
					new seqRequest(oeisTextArea, heightSeq, "Height History")
							.start();
				else
					oeisTextArea.setText(oeisTextArea.getText()
							+ "\nHeight History is trival");
				if (!areaSeq.isEmpty())
					new seqRequest(oeisTextArea, areaSeq, "Area History")
							.start();
				else
					oeisTextArea.setText(oeisTextArea.getText()
							+ "\nArea History is trival");
				if (!symmetrySeq.isEmpty())
					new seqRequest(oeisTextArea, symmetrySeq,
							"Symmetry History").start();
				else
					oeisTextArea.setText(oeisTextArea.getText()
							+ "\nSymmetry History is trival");
				if (!xsymmetrySeq.isEmpty())
					new seqRequest(oeisTextArea, xsymmetrySeq,
							"X Symmetry History").start();
				else
					oeisTextArea.setText(oeisTextArea.getText()
							+ "\nX Symmetry History is trival");
				if (!ysymmetrySeq.isEmpty())
					new seqRequest(oeisTextArea, ysymmetrySeq,
							"Y Symmetry History").start();
				else
					oeisTextArea.setText(oeisTextArea.getText()
							+ "\nY Symmetry History is trival");

				JScrollPane sp = new JScrollPane(oeisTextArea);
				sp.setPreferredSize(new Dimension(800, 500));

				JOptionPane.showMessageDialog(panel, sp, "OEIS Response",
						JOptionPane.PLAIN_MESSAGE);

			}
		});

		JLabel ruleLabel = new JLabel("Rule (S/B):");
		ruleLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);

		ruleComboBox = new JComboBox();
		ruleComboBox.setEditable(true);
		ruleComboBox.setMinimumSize(controlDimension);
		ruleComboBox.setMaximumSize(controlDimension);
		ruleComboBox.setPreferredSize(controlDimension);

		ruleComboBox.addItem("23/3");
		ruleComboBox.addItem("2468/1357");
		ruleComboBox.addItem("1357/2468");
		ruleComboBox.addItem("23/36");
		ruleComboBox.addItem("/234");
		ruleComboBox.addItem("3678/34678");
		ruleComboBox.addItem("01245678/34");
		ruleComboBox.addItem("12345/3");
		ruleComboBox.addItem("1234/37");
		ruleComboBox.addItem("125678/367");
		ruleComboBox.addItem("45678/3");
		ruleComboBox.addItem("235678/3678");
		ruleComboBox.addItem("5678/35678");

		JLabel sizeLabel = new JLabel("Size:");
		sizeLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);

		sizeComboBox = new JComboBox();
		sizeComboBox.setEditable(true);
		sizeComboBox.setMinimumSize(controlDimension);
		sizeComboBox.setMaximumSize(controlDimension);
		sizeComboBox.setPreferredSize(controlDimension);
		sizeComboBox.addItem("10x10");
		sizeComboBox.addItem("25x25");
		sizeComboBox.addItem("50x50");
		sizeComboBox.addItem("100x100");

		speedLabel = new JLabel("Speed:");
		speedLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);

		speedSlider = new JSlider();
		speedSlider.setPaintTicks(true);
		speedSlider.setMinimumSize(controlDimension);
		speedSlider.setMaximumSize(controlDimension);
		speedSlider.setPreferredSize(controlDimension);
		speedSlider.setMinimum(33);
		speedSlider.setMaximum(2000);

		speedSlider.addChangeListener(new ChangeListener() {

			@Override
			public void stateChanged(ChangeEvent arg0) {
				// if (speedSlider.getValue() > 2100)
				// speedSlider.setValue(2100);
				int delay = 2033 - speedSlider.getValue();
				nextTimer.setDelay(delay);
				speedLabel.setText("Speed: "
						+ (Math.round(10000 / (double) delay)) / 10d + "fps");
			}
		});
		speedSlider.setValue(1000);

		zoomLabel = new JLabel("Zoom (10px):");
		zoomLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
		zoomSlider = new JSlider();
		zoomSlider.setPaintTicks(true);
		zoomSlider.setMinimumSize(controlDimension);
		zoomSlider.setMaximumSize(controlDimension);
		zoomSlider.setPreferredSize(controlDimension);
		zoomSlider.setMinimum(1);
		zoomSlider.setMaximum(30);
		zoomSlider.setValue(10);

		zoomSlider.addChangeListener(new ChangeListener() {

			@Override
			public void stateChanged(ChangeEvent arg0) {
				int px = zoomSlider.getValue();
				GOLController.this.gol.setScrollFactor(px);
				zoomLabel.setText("Zoom (" + px + "px)");
			}
		});
		zoomLabel.setVisible(false);
		zoomSlider.setVisible(false);

		limiterCheckbox = new JCheckBox("fps limit", true);
		limiterCheckbox.setAlignmentX(JComponent.CENTER_ALIGNMENT);

		limiterCheckbox.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent arg0) {
				if (limiterCheckbox.isSelected()) {

					int delay = 2033 - speedSlider.getValue();
					nextTimer.setDelay(delay);
					speedLabel.setText("Speed: "
							+ (Math.round(10000 / (double) delay)) / 10d
							+ "fps");

				} else {
					nextTimer.setDelay(0);
					speedLabel.setText("Speed: xxfps");

				}
			}
		});

		scrollableCheckbox = new JCheckBox("Scrollable", false);
		scrollableCheckbox.setAlignmentX(JComponent.CENTER_ALIGNMENT);
		scrollableCheckbox.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent arg0) {
				if (scrollableCheckbox.isSelected()) {
					GOLController.this.gol.setScrollable(true);
					zoomSlider.setVisible(true);
					zoomLabel.setVisible(true);
				} else {
					GOLController.this.gol.setScrollable(false);
					zoomSlider.setVisible(false);
					zoomLabel.setVisible(false);
				}
			}
		});

		gridCheckbox = new JCheckBox("Display Grid", true);
		gridCheckbox.setAlignmentX(JComponent.CENTER_ALIGNMENT);
		gridCheckbox.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent arg0) {
				if (gridCheckbox.isSelected()) {
					GOLController.this.gol.showGrid = true;
					GOLController.this.gol.repaint();
				} else {
					GOLController.this.gol.showGrid = false;
					GOLController.this.gol.repaint();
				}
			}
		});

		JLabel patternLabel = new JLabel("Pattern:");
		patternLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);

		patternComboBox = new JComboBox();
		patternComboBox.setMinimumSize(controlDimension);
		patternComboBox.setMaximumSize(controlDimension);
		patternComboBox.setPreferredSize(controlDimension);

		patternComboBox.addItem("#clear");

		for (String file : listFiles()) {
			patternComboBox.addItem(file);
		}

		patternComboBox.setSelectedIndex(0);

		patternComboBox.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {

				pause();

				String comments = "";
				Cell[][] cellArray = null;
				int row = 0;
				int column = 0;
				if (patternComboBox.getSelectedIndex() == 0) {
					GOLController.this.gol.killAllCells();
				} else {

					String path = "patterns/";
					File pattern = new File(path
							+ patternComboBox.getSelectedItem());

					if (pattern.getName().toLowerCase().endsWith(".lif")) {
						try {
							BufferedReader br = new BufferedReader(
									new FileReader(pattern));
							String line;
							while ((line = br.readLine()) != null) {

								if (line.startsWith("#Life"))
									continue;
								else if (line.startsWith("#D")) {
									System.out.println(line.substring(line
											.indexOf('D') + 2));
									comments = comments
											+ "\n"
											+ line.substring(line.indexOf('D') + 2);

								} else if (line.startsWith("#N")) {
									ruleComboBox.setSelectedItem("23/3");
								} else if (line.startsWith("#R")) {
									String rule = line.substring(line
											.indexOf('R') + 2);
									ruleComboBox.setSelectedItem(rule);
								} else if (line.startsWith("#P")) {
									int xres = 2 * Integer.parseInt(line
											.substring(line.indexOf('P') + 2,
													line.indexOf(' ', 3)));
									int yres = 2 * Integer.parseInt(line
											.substring(line.indexOf(' ',
													line.indexOf(' ', 3)) + 1));
									sizeComboBox.setSelectedItem((xres) + "x"
											+ (yres));
									cellArray = new Cell[xres + 1][yres + 1];

								} else {
									if (line.replaceAll("\\.|\\*|X", "")
											.length() == 0) {

										for (int i = 0; i < line.length(); i++) {
											switch (line.charAt(i)) {
											case '.':
												cellArray[column][row] = Cell.DEAD;
												break;
											case '*':
												cellArray[column][row] = Cell.LIVE;
												break;
											case 'X':
												cellArray[column][row] = Cell.UNINHABITABLE;
												break;

											default:
												break;
											}
											column++;
										}

									} else {
										System.out.println("Parse Error on: "
												+ line.replaceAll("\\.|\\*", ""));
									}
									row++;
									column = 0;
								}
							}
							if (cellArray.length != 0)
								GOLController.this.gol.setCellArray(cellArray);

							br.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					} else if (pattern.getName().toLowerCase()
							.endsWith(".cells")) {

						try {
							BufferedReader br = new BufferedReader(
									new FileReader(pattern));
							String line;
							int xres = 0;
							int yres = 0;

							while ((line = br.readLine()) != null) {
								if (line.startsWith("!"))
									continue;
								else if (line.replaceAll("\\.|O", "").length() == 0) {
									yres++;
									if (line.length() > xres)
										xres = line.length();

								} else {
									System.out.println("Parse Error on: "
											+ line.replaceAll("\\.|O", ""));
								}
							}
							cellArray = new Cell[xres + 2][yres + 2];

							for (int i = 0; i < cellArray.length; i++) {
								Arrays.fill(cellArray[i], Cell.DEAD);
							}

							sizeComboBox.setSelectedItem((xres + 2) + "x"
									+ (yres + 2));

							br.close();
							br = new BufferedReader(new FileReader(pattern));

							int cellCount = 0;
							while ((line = br.readLine()) != null) {

								if (line.startsWith("!"))
									continue;
								else {
									if (line.replaceAll("\\.|O", "").length() == 0) {

										for (int i = 0; i < line.length(); i++) {
											switch (line.charAt(i)) {
											case '.':
												cellArray[column + 1][row + 1] = Cell.DEAD;
												break;
											case 'O':
												cellArray[column + 1][row + 1] = Cell.LIVE;
												cellCount++;
												break;

											default:
												break;
											}
											column++;
										}
									} else {
										System.out.println("Parse Error on: "
												+ line.replaceAll("\\.|\\*", ""));
									}
									row++;
									column = 0;
								}
							}
							if (cellArray.length != 0)
								System.out.println(cellCount);

							GOLController.this.gol.setCellArray(cellArray);
							br.close();
						} catch (IOException e) {
							e.printStackTrace();
						}

					}

					if (comments.length() != 0)
						JOptionPane.showMessageDialog(panel, comments);

				}

			}

		});

		sizeComboBox.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				String dimensions = sizeComboBox.getSelectedItem().toString();
				dimensions = dimensions.toLowerCase().replaceAll("[^0-9x]", "");

				if (dimensions.contains("x")) {
					int columns = Integer.parseInt(dimensions.substring(0,
							dimensions.indexOf("x")));
					int rows = Integer.parseInt(dimensions.substring(dimensions
							.indexOf("x") + 1));

					GOLController.this.gol.setGridSize(columns, rows);

				} else {
					int val = Integer.parseInt(dimensions);
					sizeComboBox.setSelectedItem(val + "x" + val);
				}
			}
		});

		// sizeComboBox.addItemListener(new ItemListener() {
		// @Override
		// public void itemStateChanged(ItemEvent arg0) {
		// }
		// });

		sizeComboBox.setSelectedItem("25x25"); // set initial size

		ruleComboBox.addItemListener(new ItemListener() {
			@Override
			public void itemStateChanged(ItemEvent arg0) {
				String dimensions = ruleComboBox.getSelectedItem().toString();
				GOLController.this.gol.setRules(
						dimensions.substring(0, dimensions.indexOf("/")),
						dimensions.substring(dimensions.indexOf("/") + 1));

			}
		});

		nBorder = new JComboBox();
		nBorder.setAlignmentX(JComponent.RIGHT_ALIGNMENT);
		nBorder.setMinimumSize(controlDimension);
		nBorder.setMaximumSize(controlDimension);
		nBorder.setPreferredSize(controlDimension);
		nBorder.addItem("Extend");
		nBorder.addItem("Y Torus");
		nBorder.addItem("Wall");

		nBorder.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				String option = nBorder.getSelectedItem().toString();
				if (option.equals("Y Torus")) {
					sBorder.setSelectedItem("Y Torus");
					GOLController.this.gol.ytorus = true;
				} else if (option.equals("Extend")) {
					GOLController.this.gol.ytorus = false;
					if (sBorder.getSelectedItem().equals("Y Torus"))
						sBorder.setSelectedItem("Extend");
					GOLController.this.gol.northExpand = true;
				} else if (option.equals("Wall")) {
					GOLController.this.gol.ytorus = false;
					GOLController.this.gol.northExpand = false;
					if (sBorder.getSelectedItem().equals("Y Torus"))
						sBorder.setSelectedItem("Wall");
				}
			}
		});

		eBorder = new JComboBox();
		eBorder.setAlignmentX(JComponent.RIGHT_ALIGNMENT);
		eBorder.setMinimumSize(controlDimension);
		eBorder.setMaximumSize(controlDimension);
		eBorder.setPreferredSize(controlDimension);
		eBorder.addItem("Extend");
		eBorder.addItem("X Torus");
		eBorder.addItem("Wall");

		eBorder.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				String option = eBorder.getSelectedItem().toString();
				if (option.equals("X Torus")) {
					wBorder.setSelectedItem("X Torus");
					GOLController.this.gol.xtorus = true;
				} else if (option.equals("Extend")) {
					GOLController.this.gol.xtorus = false;
					if (wBorder.getSelectedItem().equals("X Torus"))
						wBorder.setSelectedItem("Extend");
					GOLController.this.gol.eastExpand = true;
				} else if (option.equals("Wall")) {
					GOLController.this.gol.xtorus = false;
					GOLController.this.gol.eastExpand = false;
					if (wBorder.getSelectedItem().equals("X Torus"))
						wBorder.setSelectedItem("Wall");
				}
			}
		});

		sBorder = new JComboBox();
		sBorder.setAlignmentX(JComponent.RIGHT_ALIGNMENT);
		sBorder.setMinimumSize(controlDimension);
		sBorder.setMaximumSize(controlDimension);
		sBorder.setPreferredSize(controlDimension);
		sBorder.addItem("Extend");
		sBorder.addItem("Y Torus");
		sBorder.addItem("Wall");

		sBorder.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				String option = sBorder.getSelectedItem().toString();
				if (option.equals("Y Torus")) {
					nBorder.setSelectedItem("Y Torus");
					GOLController.this.gol.ytorus = true;
				} else if (option.equals("Extend")) {
					GOLController.this.gol.ytorus = false;
					if (nBorder.getSelectedItem().equals("Y Torus"))
						nBorder.setSelectedItem("Extend");
					GOLController.this.gol.southExpand = true;
				} else if (option.equals("Wall")) {
					GOLController.this.gol.ytorus = false;
					GOLController.this.gol.southExpand = false;
					if (nBorder.getSelectedItem().equals("Y Torus"))
						nBorder.setSelectedItem("Wall");

				}
			}
		});

		wBorder = new JComboBox();
		wBorder.setAlignmentX(JComponent.RIGHT_ALIGNMENT);
		wBorder.setMinimumSize(controlDimension);
		wBorder.setMaximumSize(controlDimension);
		wBorder.setPreferredSize(controlDimension);
		wBorder.addItem("Extend");
		wBorder.addItem("X Torus");
		wBorder.addItem("Wall");

		wBorder.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				String option = wBorder.getSelectedItem().toString();
				if (option.equals("X Torus")) {
					eBorder.setSelectedItem("X Torus");
					GOLController.this.gol.xtorus = true;
				} else if (option.equals("Extend")) {
					GOLController.this.gol.xtorus = false;
					if (eBorder.getSelectedItem().equals("X Torus"))
						eBorder.setSelectedItem("Extend");
					GOLController.this.gol.westExpand = true;
				} else if (option.equals("Wall")) {
					GOLController.this.gol.xtorus = false;
					GOLController.this.gol.westExpand = false;
					if (eBorder.getSelectedItem().equals("X Torus"))
						eBorder.setSelectedItem("Wall");

				}
			}
		});
		JLabel borderLabel = new JLabel("Borders:");
		borderLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
		JPanel nPanel = new JPanel(new BorderLayout());
		JPanel ePanel = new JPanel(new BorderLayout());
		JPanel sPanel = new JPanel(new BorderLayout());
		JPanel wPanel = new JPanel(new BorderLayout());

		JLabel nLabel = new JLabel("N:  ");
		JLabel eLabel = new JLabel("E:  ");
		JLabel sLabel = new JLabel("S:  ");
		JLabel wLabel = new JLabel("W: ");

		nPanel.add(nLabel, BorderLayout.WEST);
		nPanel.add(nBorder, BorderLayout.CENTER);
		sPanel.add(sLabel, BorderLayout.WEST);
		sPanel.add(sBorder, BorderLayout.CENTER);
		ePanel.add(eLabel, BorderLayout.WEST);
		ePanel.add(eBorder, BorderLayout.CENTER);
		wPanel.add(wLabel, BorderLayout.WEST);
		wPanel.add(wBorder, BorderLayout.CENTER);
		nPanel.setMaximumSize(controlDimension);
		sPanel.setMaximumSize(controlDimension);
		ePanel.setMaximumSize(controlDimension);
		wPanel.setMaximumSize(controlDimension);

		JPanel timerPanel = new JPanel(new BorderLayout());
		timerPanel.add(timerButton, BorderLayout.CENTER);
		timerPanel.add(searchButton, BorderLayout.EAST);
		timerPanel.setMaximumSize(controlDimension);

		JPanel stepPanel = new JPanel(new BorderLayout());
		stepPanel.add(stepButton, BorderLayout.CENTER);
		stepPanel.add(seqButton, BorderLayout.EAST);
		stepPanel.setMaximumSize(controlDimension);

		panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
		panel.add(Box.createVerticalStrut(2));

		panel.add(timerPanel);
		panel.add(stepPanel);
		panel.add(undoButton);
		panel.add(speedLabel);
		panel.add(speedSlider);
		panel.add(limiterCheckbox);
		panel.add(scrollableCheckbox);
		panel.add(zoomLabel);
		panel.add(zoomSlider);
		panel.add(Box.createGlue());
		panel.add(ruleLabel);
		panel.add(ruleComboBox);
		panel.add(sizeLabel);
		panel.add(sizeComboBox);
		panel.add(patternLabel);
		panel.add(patternComboBox);
		panel.add(saveButton);
		panel.add(Box.createGlue());
		panel.add(gridCheckbox);
		panel.add(borderLabel);
		panel.add(nPanel);
		panel.add(sPanel);
		panel.add(ePanel);
		panel.add(wPanel);
	}

	protected int iterate() {
		int period = GOLController.this.gol.hashCheck();
		GOLController.this.gol.doGeneration();

		if ((period > 0) && periodPrompt) {
			pause();
			JOptionPane.showMessageDialog(panel, "Periodicity detected over "
					+ period + " generations");
		}
		

		return period;
	}

	public void start() {
		nextTimer.restart();
		timerButton.setText("Pause");
	}

	public void pause() {
		nextTimer.stop();
		timerButton.setText("\u25BA");
	}

	public JPanel getPanel() {
		return panel;
	}

	private void saveFile(String filename) {// TODO modify to keep to spec

		try {
		    String path = "patterns/";
	        File folder = new File(path);
		    folder.mkdir();
		    
			File file = new File(path + filename);
			if (!file.exists())
				file.createNewFile();

			BufferedWriter bw = new BufferedWriter(new FileWriter(file));
			bw.write("#Life" + "\n");
			bw.write("#R " + ruleComboBox.getSelectedItem() + "\n");

			Cell[][] grid = GOLController.this.gol.getCellArray();
			bw.write("#P " + grid.length / 2 + " " + grid[0].length / 2 + "\n");

			String linebuffer;
			for (int i = 0; i < grid[0].length; i++) {
				linebuffer = "";

				for (int j = 0; j < grid.length; j++) {
					if (grid[j][i] == Cell.LIVE)
						linebuffer += "*";
					else if (grid[j][i] == Cell.DEAD)
						linebuffer += "."; // TODO leave erroneous '.'s
					else if (grid[j][i] == Cell.UNINHABITABLE)
						linebuffer += "X";

				}
				bw.write(linebuffer + "\n");
			}
			bw.close();
		} catch (IOException e1) {
			e1.printStackTrace();
		}

	}

	private ArrayList<String> listFiles() {
		ArrayList<String> files = new ArrayList<String>();

		String path = "patterns";

		String file;
		File folder = new File(path);
		//folder.mkdir();
		if (folder.exists()) {
			File[] listOfFiles = folder.listFiles();

			for (int i = 0; i < listOfFiles.length; i++) {

				if (listOfFiles[i].isFile()) {
					file = listOfFiles[i].getName();
					if ((file.toLowerCase().endsWith(".lif"))
							|| (file.toLowerCase().endsWith(".cells"))) {
						files.add(file);
					}
				}
			}
		}
		return files;
	}
}
